#ifndef     __DMA_H
#define     __DMA_H

#include "./compat.h"

namespace device::dma {

#define DMA_IFCR_FLAGS(x)       (DMA_IFCR_CGIF##x + DMA_IFCR_CTCIF##x + DMA_IFCR_CHTIF##x + DMA_IFCR_CTEIF##x)

#define DMA(BASE)               reinterpret_cast<DMA_TypeDef *>(BASE)
#define DMA_CHAN(BASE)          reinterpret_cast<DMA_Channel_TypeDef *>(BASE)

template <uint32_t BASE, uint8_t PRIORITY = 0, uint8_t SIZE = 0>
struct DmaChan {

    inline static void init_with_write_uart(uint32_t memory, uint32_t periph, uint8_t len) {
        init(memory , periph, len);
        DMA_CHAN(BASE)->CCR = (PRIORITY << 12) +
            (SIZE << 10) +
            (SIZE << 8) +
            (0x01 << 7) + // MINC
            (0x01 << 4) + // 写
            (0x01 << 1) + // 中断
            (0x01);
    }

    inline static void init_with_recv_uart(uint32_t memory, uint32_t periph, uint32_t len) {
        init(memory, periph, len);
        DMA_CHAN(BASE)->CCR = (PRIORITY << 12) +
            (SIZE << 10) +
            (SIZE << 8) +
            (0x01 << 7) + // MINC
            (0x01 << 5) + // 循环模式
            (0x01);
    }

    inline static uint32_t dntr() {
        return DMA_CHAN(BASE)->CNDTR;
    }

    inline static void init_with_adc(uint32_t memory, uint32_t periph, uint8_t count) {
        init(memory, periph, count);
        DMA_CHAN(BASE)->CCR = (PRIORITY << 12) +
            (SIZE << 10) +
            (SIZE << 8) +
            (0x01 << 7) + // MINC
            (0x01 << 5) + // 循环模式
            (0x01);
    }

private:
    inline static void init(uint32_t memory, uint32_t periph, uint16_t len) {
        DMA_CHAN(BASE)->CCR = 0x00;
        DMA_CHAN(BASE)->CMAR = memory;
        DMA_CHAN(BASE)->CNDTR = len;
        DMA_CHAN(BASE)->CPAR = periph;
    }
};

template <uint32_t BASE, uint8_t CHAN>
struct Dma {

    inline static void clear_flags() {
        static_assert((CHAN >= 1) && (CHAN <= 7), "CHAN fail");
        if constexpr (CHAN == 1) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(1);
        } else if constexpr (CHAN == 2) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(2);
        } else if constexpr (CHAN == 3) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(3);
        } else if constexpr (CHAN == 4) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(4);
        } else if constexpr (CHAN == 5) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(5);
        } else if constexpr (CHAN == 6) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(6);
        } else if constexpr (CHAN == 7) {
            DMA(BASE)->IFCR = DMA_IFCR_FLAGS(7);
        }
    }

    inline static bool is_complete() {
        static_assert((CHAN >= 1) && (CHAN <= 7), "CHAN fail");
        if constexpr (CHAN == 1) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF1) != 0;
        } else if constexpr (CHAN == 2) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF2) != 0;
        } else if constexpr (CHAN == 3) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF3) != 0;
        } else if constexpr (CHAN == 4) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF4) != 0;
        } else if constexpr (CHAN == 5) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF5) != 0;
        } else if constexpr (CHAN == 6) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF6) != 0;
        } else if constexpr (CHAN == 7) {
            return (DMA(BASE)->ISR & DMA_ISR_TCIF7) != 0;
        }
    }
};

}



#endif
